msg_tool\scripts\yuris\arc/
pe.rs

1use anyhow::Result;
2use pelite::{PeFile, Wrap};
3
4const YSER_MAGIC: &[u8; 4] = b"YSER";
5
6/// Find the YPF archive base offset inside a PE (EXE) file.
7///
8/// Searches the PE overlay for the "YSER" header signature at 0x10-aligned
9/// boundaries, then reads the 32-bit header size field at offset+4 and returns
10/// `YSER_offset + header_size` as the start of the YPF data.
11pub fn get_base_offset<D: AsRef<[u8]> + ?Sized>(data: &D) -> Result<u64> {
12    let file = PeFile::from_bytes(data)?;
13    let last_section_end = file
14        .section_headers()
15        .iter()
16        .map(|s| s.PointerToRawData + s.SizeOfRawData)
17        .max()
18        .unwrap_or_else(|| match file.optional_header() {
19            Wrap::T32(h) => h.SizeOfHeaders,
20            Wrap::T64(h) => h.SizeOfHeaders,
21        });
22    let aligned_offset = ((last_section_end + 0xF) & !0xF) as usize;
23    let data = data.as_ref();
24    if aligned_offset + 8 > data.len() {
25        anyhow::bail!("No overlay for pe image.");
26    }
27    for i in (aligned_offset..(data.len() - 8)).step_by(0x10) {
28        if &data[i..i + 4] == YSER_MAGIC {
29            let header_size =
30                u32::from_le_bytes([data[i + 4], data[i + 5], data[i + 6], data[i + 7]]);
31            return Ok(i as u64 + header_size as u64);
32        }
33    }
34    anyhow::bail!("Failed to find YSER header in pe file.")
35}